package tomasulo;
import java.util.LinkedList;

class Instruction {
	static final int ADDD = 0;
	static final int SUBD = 1;
	static final int MULD = 2;
	static final int DIVD = 3;
	static final int LD = 4;
	static final int ST = 5;
	static final int END = 6;
	static final String STR_ADDD = "ADDD";
	static final String STR_SUBD = "SUBD";
	static final String STR_MULD = "MULD";
	static final String STR_DIVD = "DIVD";
	static final String STR_LD = "LD";
	static final String STR_ST = "ST";
	static final String STR_END = "END";

	static final String[] OPE = { STR_ADDD, STR_SUBD, STR_MULD, STR_DIVD,
			STR_LD, STR_ST };

	int type;
	int op1, op2, dst;// regNo
	int addr;
	int stationNo;

	static final int AVALIABLE = -1;
	static final int EXEING = -2;
	static final int FINISHED = -3;

	static final String FORMAT_ERROR = "Intruction Format Error!";

	int state;
	boolean valid; //判断指令是否合法
	int finishedTime;

	public void setState(int state) {
		this.state = state;
	}

	public void setFinishTime(int time) {
		this.finishedTime = time;
	}

	int getRegIdx(String str) { // 从字符串指令中获取int型的寄存器编号
		if (str.length() != 2) {
			return -1;
		} else {
			int idx = Integer.parseInt(String.valueOf(str.charAt(1)));
			if (idx < 0 || idx > Register.DEFAULT_SIZE) {
				idx = -1;
			}
			return idx;
		}
	}

	Instruction(String str) {
		valid = true;
		state = AVALIABLE;
		finishedTime = -1;
		String sub[] = str.split("\\s+");
		if (sub.length == 1) {
			if (sub[0].equals(STR_END)) {
				this.type = END;
			} else {
				valid = false;
				System.out.println(FORMAT_ERROR);
			}
		} else if (sub.length == 3) {
			if (sub[0].equals(STR_LD)) {
				this.type = LD;
				if ((op1 = getRegIdx(sub[1])) == -1) {
					valid = false;
				}
				this.addr = Integer.valueOf(sub[2]);
			} else if (sub[0].equals(STR_ST)) {
				this.type = ST;
				if ((op1 = getRegIdx(sub[1])) == -1) {
					valid = false;
				}
				this.addr = Integer.valueOf(sub[2]);
			} else {
				valid = false;
				System.out.println(FORMAT_ERROR);
			}
		} else if (sub.length == 4) {
			int i;
			for (i = 0; i < 4; i++) {
				if (sub[0].equals(OPE[i])) {
					this.type = i;
					this.op1 = getRegIdx(sub[1]);
					this.op2 = getRegIdx(sub[2]);
					this.dst = getRegIdx(sub[3]);
					if (op1 == -1 || op2 == -1 || dst == -1) {
						valid = false;
						System.out.println(FORMAT_ERROR);
					} else {
						break;
					}
				}
			}
			if (i == 4) {
				valid = false;
				System.out.println(FORMAT_ERROR);
			}
		} else {
			valid = false;
			System.out.println(FORMAT_ERROR);
		}
	}

	String[] toStrings() {
		String ret[];
		ret = new String[5];

                ret[0]="";
		if (type == 0 || type == 1 || type == 2 || type == 3) {
			ret[1] = OPE[type];
			if (op1 == -1)
				ret[2] = "";
			else
				ret[2] = "F" + op1;
			if (op2 == -1)
				ret[3] = "";
			else
				ret[3] = "F" + op2;
			if (dst == -1)
				ret[4] = "";
			else
				ret[4] = "F" + dst;
		} else if (type == 4 || type == 5) {
			ret[1] = OPE[type];
			if (op1 == -1)
				ret[2] = "";
			else
				ret[2] = "F" + op1;
			ret[3] = String.valueOf(addr);
			ret[4] = "";
		} else {
			ret[1] = "END";
			ret[2] = ret[3] = ret[4] = "";
		}
		return ret;
	}
}

class InstructionManager {
	LinkedList<Instruction> queue;
	int pc;
	boolean finished;

	public void addInstruction(String str) {
		Instruction inst = new Instruction(str.replace(',', ' '));
		if (inst.valid == true) {
			queue.addLast(inst);
			System.out.println("> Instr \"" + str + "\" added");
		}
	}

	public void setState(int idx, int state) {
		Instruction inst = queue.get(idx);
		inst.setState(state);
	}

	public void setFinishTime(int idx, int time) {
		Instruction inst = queue.get(idx);
		inst.setFinishTime(time);
	}

	public void clear() {
		queue.clear();
                finished=false;
                pc = 0;

	}

	void click() {
		System.out.println("[Manager.click]");
		if (pc >= queue.size())
			return;
		if (!finished) {
			System.out.print("Send > "+ pc);
                        System.out.print(queue.size());
			Instruction instr = queue.get(pc);
			int ret = -1;
			if (instr.state == Instruction.AVALIABLE) {
				int stationNo = -1;
				//System.out.print(instr + " type:" + instr.type);
				switch (instr.type) {
				case Instruction.ADDD: // 加减操作
				case Instruction.SUBD:
					System.out.println("> ADDD|SUBD");
					stationNo = Kernel.kernel.add.addTask(instr, pc);
					System.out.println("addTask " + stationNo);
					if (stationNo != -1) {
						instr.stationNo = stationNo;
						Kernel.kernel.register.stationNo[instr.dst] = stationNo;
						Kernel.kernel.register.busy[instr.dst] = true;
						ret = 0;
					}
					break;
				case Instruction.MULD: // 乘除操作
				case Instruction.DIVD: 
					System.out.println("> MULD|DIVD");
					stationNo = Kernel.kernel.mul.addTask(instr, pc);
					if (stationNo != -1) {
						instr.stationNo = stationNo;
						Kernel.kernel.register.stationNo[instr.dst] = stationNo;
						Kernel.kernel.register.busy[instr.dst] = true;
						ret = 0;
					}
					break;
				case Instruction.LD:// load操作
					System.out.println("> LD");
					stationNo = Kernel.kernel.memory.load(instr.addr, pc);
					if (stationNo != -1) {
						instr.stationNo = stationNo;
						Kernel.kernel.register.stationNo[instr.op1] = stationNo;
						Kernel.kernel.register.busy[instr.op1] = true;
						ret = 0;
					}
					break;
				case Instruction.ST:// store操作
					System.out.println("> ST");
					if (Kernel.kernel.register.busy[instr.op1]) { // 如果目标寄存器忙碌
						int StationNoForReg = Kernel.kernel.register.stationNo[instr.op1];
						stationNo = Kernel.kernel.memory.store(StationNoForReg,
								instr.addr, pc);
						if (stationNo != -1) {
							instr.stationNo = stationNo;
							ret = 0;
						}
					} else { // 不忙碌
						double value = Kernel.kernel.register.val[instr.op1];
						stationNo = Kernel.kernel.memory.store(value,
								instr.addr, pc);
						if (stationNo != -1) {
							instr.stationNo = stationNo;
							ret = 0;
						}
					}
					break;
				case Instruction.END:// END
					System.out.println("> END");
					finished = true;
					break;
				default:
					System.out.println();
				}

				if (ret == 0) {
					instr.setState(Instruction.EXEING);
					pc++;
					if (pc == queue.size()) {
						finished = true;
					}
				}
			}
		}
	}

	String[][] toStrings() {
		String ret[][] = new String[queue.size()][5];
		for (int i = 0; i < queue.size(); i++) {
			ret[i] = queue.get(i).toStrings();
                        ret[i][0]=String.valueOf(i);
		}
		return ret;
	}

	String[][] getRunningState() {
		String[][] ret = new String[pc][4];
		for (int i = 0; i < pc; i++) {
			ret[i][0] = String.valueOf(i);
			Instruction instr = queue.get(i);
			if (instr.state == Instruction.FINISHED) {
				ret[i][1] = "FINISHED";
				if (instr.type == 0 || instr.type == 1||instr.type == 2|| instr.type == 3){
					ret[i][2]=String.valueOf(instr.finishedTime);
					ret[i][3]="";
				}else if(instr.type == 4 || instr.type == 5){
					ret[i][2]="";
					ret[i][3]=String.valueOf(instr.finishedTime);
				}else{
                                    ret[i][2]=ret[i][3]="";
                                }
			} else if(instr.state == Instruction.EXEING){
				ret[i][1] = "EXEING";
				if (instr.type == 0 || instr.type == 1){
					ret[i][2]=Kernel.kernel.add.instrState(instr.stationNo);
					ret[i][3]="";
				}else if( instr.type == 2|| instr.type == 3) {
					ret[i][2]=Kernel.kernel.mul.instrState(instr.stationNo);
					ret[i][3]="";
				}else if(instr.type == 4 || instr.type == 5){
					ret[i][2]="";
					ret[i][3]=Kernel.kernel.memory.instrState(i);
				}
			}
		}
		return ret;
	}

	InstructionManager() {
		queue = new LinkedList<Instruction>();
		pc = 0;
                finished=false;
	}
}